/* @flow */

// can we use __proto__?
export const hasProto = '__proto__' in {}

// Browser environment sniffing
/**
 * 2019-05-15
 * 检测当前宿主环境是否是浏览器
 */
export const inBrowser = typeof window !== 'undefined'
export const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform
export const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase()
/**
 * 2019-05-15
 * 获取当前浏览器的 UA 字符串
 */
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
/**
 * 2019-05-15
 * 判断当前浏览器是否是 Internet Explorer 浏览器
 */
export const isIE = UA && /msie|trident/.test(UA)
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
export const isEdge = UA && UA.indexOf('edge/') > 0
export const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android')
export const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios')
export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
export const isPhantomJS = UA && /phantomjs/.test(UA)
export const isFF = UA && UA.match(/firefox\/(\d+)/)

/**
 * 2019-05-15
 * 在 Firefox 中原生提供了 Object.prototype.watch 函数，
 * 所以当运行在 Firefox 中时 nativeWatch 为原生提供的函数，
 * 在其他浏览器中 nativeWatch 为 undefined。
 * 这个变量主要用于 Vue 处理 watch 选项时与其冲突
 */
// Firefox has a "watch" function on Object.prototype...
export const nativeWatch = ({}).watch

export let supportsPassive = false
if (inBrowser) {
  try {
    const opts = {}
    Object.defineProperty(opts, 'passive', ({
      get () {
        /* istanbul ignore next */
        supportsPassive = true
      }
    }: Object)) // https://github.com/facebook/flow/issues/285
    window.addEventListener('test-passive', null, opts)
  } catch (e) {
  }
}

/**
 * 2019-05-15
 * isServerRendering 函数的执行结果是一个布尔值，用来判断是否是服务端渲染
 */
// this needs to be lazy-evaled because vue may be required before
// vue-server-renderer can set VUE_ENV
let _isServer
export const isServerRendering = () => {
  if (_isServer === undefined) {
    /* istanbul ignore if */
    if (!inBrowser && !inWeex && typeof global !== 'undefined') {
      // detect presence of vue-server-renderer and avoid
      // Webpack shimming the process
      _isServer = global['process'] && global['process'].env.VUE_ENV === 'server'
    } else {
      _isServer = false
    }
  }
  return _isServer
}

// detect devtools
export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__

/* istanbul ignore next */
export function isNative(Ctor: any): boolean {
  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}

/**
 * 2019-05-15
 * 用来判断当前宿主环境是否支持原生 Symbol 和 Reflect.ownKeys 的可用性
 */
export const hasSymbol =
  typeof Symbol !== 'undefined' && isNative(Symbol) &&
  typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys)

let _Set
/* istanbul ignore if */ // $flow-disable-line
if (typeof Set !== 'undefined' && isNative(Set)) {
  // use native Set when available.
  _Set = Set
} else {
  // a non-standard Set polyfill that only works with primitive keys.
  _Set = class Set implements SimpleSet {
    set: Object;

    constructor() {
      this.set = Object.create(null)
    }

    has(key: string | number) {
      return this.set[key] === true
    }

    add(key: string | number) {
      this.set[key] = true
    }

    clear() {
      this.set = Object.create(null)
    }
  }
}

export
interface SimpleSet {
  has(key: string | number): boolean;
  add(key: string | number): mixed;
  clear(): void;
}

export {_Set}
